#include "BackupFSDB.h"
#include "../logger/BackupFSLogger.h"
#include "../BackupFSUtils.h"
namespace BackupFS
{

	BackupFSDB::BackupFSDB()
	{
		//create table files
		_InitSqls.push_back(
			"CREATE TABLE IF NOT EXISTS files("
			"id INTEGER PRIMARY KEY NOT NULL,"
			"name TEXT NOT NULL,"
			"is_folder BOOLEAN NOT NULL,"
			"parent_id INTEGER NOT NULL,"
			"file_size INTEGER NOT NULL,"
			"attributes INTEGER NOT NULL,"
			"create_time INTEGER NOT NULL,"
			"modify_time INTEGER NOT NULL,"
			"access_time INTEGER NOT NULL,"
			"security BLOB NULL);");

		//create table data_blocks
		_InitSqls.push_back(
			"CREATE TABLE IF NOT EXISTS data_blocks("
			"id INTEGER PRIMARY KEY NOT NULL,"
			"file_id INTEGER NOT NULL,"
			"offset_in_file INTEGER NOT NULL,"
			"offset_in_data INTEGER NOT NULL,"
			"length INTEGER NOT NULL,"
			"attr INTEGER NOT NULL);");
		//create table fsinfo
		_InitSqls.push_back(
			"CREATE TABLE IF NOT EXISTS fsinfo("
			"id INTEGER PRIMARY KEY NOT NULL,"			
			"current_node_id INTEGER NOT NULL,"
			"current_block_id INTEGER NOT NULL,"
			"size INTEGER NOT NULL,"
			"used_size INTEGER NOT NULL);");
		_InitSqls.push_back(
			"insert into fsinfo ("
			"id,current_node_id,current_block_id,size,used_size) "
			" values(1,2,1,0,0);");
		//create index
		_InitSqls.push_back("CREATE INDEX idx_files_name ON files (name);");
		_InitSqls.push_back("CREATE INDEX idx_data_block_file_id ON data_blocks (file_id);");
	}
	BackupFSDB::~BackupFSDB()
	{
		Close();
	}
	void BackupFSDB::Close()
	{
		if (_Db)
		{
			sqlite3_close(_Db);
			_Db = nullptr;
		}
	}
	bool BackupFSDB::Init(const std::wstring& file_name, bool& has_db)
	{
		_FileName = file_name;
		int rc = sqlite3_open(W2S(file_name).c_str(), &_Db);
		if (rc != SQLITE_OK)
		{
			Logger->Error("open file ", W2S(file_name), " failed,", rc);
			return false;
		}
		has_db = true;
		//init the tables
		if (!TableExist("files"))
		{
			has_db = false;
			for (auto sql : _InitSqls)
			{
				if (!Execute(sql))
				{
					return false;
				}
			}
		}
		return true;

	}
	bool BackupFSDB::Select(const std::string& sql, std::function<void(sqlite3_stmt*)> callback)
	{
		sqlite3_stmt* stmt;
		int rc = sqlite3_prepare_v2(_Db, sql.c_str(), -1, &stmt, nullptr);
		if (rc != SQLITE_OK) 
		{
			Logger->Error("Failed to prepare statement: ", sqlite3_errmsg(_Db));
			return false;
		}

		while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) 
		{
			callback(stmt);
		}

		if (rc != SQLITE_DONE) 
		{
			Logger->Error("Failed to execute statement: ", sqlite3_errmsg(_Db));
			sqlite3_finalize(stmt);
			return false;
		}

		sqlite3_finalize(stmt);
		return true;
	}
	bool BackupFSDB::TableExist(const std::string& table_name)
	{
		std::string sql = "SELECT name FROM sqlite_master WHERE type='table' AND name='" + table_name + "';";
		bool exists = false;
		Select(sql, [&exists](sqlite3_stmt* stmt) {
			exists = true;
			});
		return exists;
	}
	bool BackupFSDB::Execute(const std::string& sql)
	{
		char* errmsg = nullptr;
		bool ok = true;
		int rc = sqlite3_exec(_Db, sql.c_str(), nullptr, nullptr, &errmsg);
		if (rc != SQLITE_OK)
		{
			ok = false;
			Logger->Error("Failed to execute sql,", errmsg ? errmsg : "unknown"," sql is ",sql);
		}
		if (errmsg)
		{
			sqlite3_free(errmsg);
		}
		return ok;
	}
}